#include "GL/glew.h"
#include "GL/glfw.h"
#include "ShaderProvider.hpp"
#include <string>
#include <cstring>
#include <iostream>

char* ShaderProvider::VERSION_STRING = "#version 410\n";

/* Läs sträng från fil, från wikibooks "opengl" */
char* ShaderProvider::ReadFile(const char* filename, int& size)
{
	
	FILE* in = fopen(filename, "rb");
	if (in == NULL) 
	{
		std::cout << "File not found!" << std::endl;
		return NULL;
	}
 
	int res_size = BUFSIZ;
	char* res = (char*)malloc(res_size);
	int nb_read_total = 0;
 
	while (!feof(in) && !ferror(in)) 
	{
		if (nb_read_total + BUFSIZ > res_size) 
		{
			if (res_size > 10*1024*1024) break;
			res_size = res_size * 2;
			res = (char*)realloc(res, res_size);
		}
		char* p_res = res + nb_read_total;
		nb_read_total += fread(p_res, 1, BUFSIZ, in);
	}
 
	fclose(in);
	res = (char*)realloc(res, nb_read_total + 1);
	size = nb_read_total + 1;
	res[nb_read_total] = '\0';
	return res;
}

/* Skapa en shader med två filnamn, delvis tagen från wikibooks opengl */
GLuint ShaderProvider::CreateShader(const char* vertexSource, const char* fragmentSource)
{
	//glfwInit();
	//glewInit();
	
	GLint compile_ok = GL_FALSE, link_ok = GL_FALSE;
	
	/* Kompilera vertexshadern */
	GLuint vs = glCreateShader(GL_VERTEX_SHADER);
	std::cout<<"YEAH"<<std::endl;
	int vertex_size = 0;
	const char *vs_source_file = ReadFile(vertexSource, vertex_size);
	const char** vs_sources = new const char*[2];
	vs_sources[0] = GetGLSLVersion();
	vs_sources[1] = vs_source_file;
	
	glShaderSource(vs, 2, (vs_sources), NULL);
	glCompileShader(vs);
	glGetShaderiv(vs, GL_COMPILE_STATUS, &compile_ok);
	PrintLog(vs);
	
	/* Blev det något fel? */
	if (!compile_ok) 
	{
		printf("Error, vertex shader: ");
		PrintLog(vs);
		return 0;
	}
	
	/* Kompilera fragmentshadern */
	GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
	int fragment_size = 0;
	const char *fs_source_file = ReadFile(fragmentSource,fragment_size);
	
	const char** fs_sources = new const char*[2];
	fs_sources[0] = GetGLSLVersion();
	fs_sources[1] = fs_source_file;
	
	glShaderSource(fs, 2, fs_sources, NULL);
	glCompileShader(fs);
	glGetShaderiv(fs, GL_COMPILE_STATUS, &compile_ok);
	PrintLog(fs);
	/* Blev något fel? */
	if (!compile_ok) 
	{
		printf("Error, fragment shader: ");
		PrintLog(fs);
		return 0;
	}
	
	/* Länka programmet */
	GLuint program = glCreateProgram();
	glAttachShader(program, vs);
	glAttachShader(program, fs);
	glLinkProgram(program);
	glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
	PrintLog(program);
	/* Skriv ut eventuella fel */
	if (!link_ok) 
	{
		printf("Link error: ");
		PrintLog(program);
		return false;
	}
	glBindFragDataLocation(program, 0, "FragmentColor");
	
	//delete [] vs_source;
	delete [] fs_sources;
	return program;
}

/* Skriv ut eventuella kompilerings- eller länkningsfel, taget från wikibooks opengl */
void ShaderProvider::PrintLog(GLuint object) const
{
	GLint log_length = 0;
	if (glIsShader(object))
		glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
	else if (glIsProgram(object))
		glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length);
	else {
		fprintf(stderr, "printlog: Not a shader or a program\n");
		return;
	}
	 
	char* log = (char*)malloc(log_length);

	if (glIsShader(object))
		glGetShaderInfoLog(object, log_length, NULL, log);
	else if (glIsProgram(object))
		glGetProgramInfoLog(object, log_length, NULL, log);
	 
	fprintf(stderr, "%s", log);
	free(log);
}

bool ShaderProvider::SetGLSLVersion(const char* version)
{
	return true;
	if(    !(version[0] == '1' || version[0] == '2' || version[0] == '3' || version[0] == '4')
		|| !(version[1] == '0' || version[1] == '1' || version[1] == '2' || version[1] == '3' || version[1] == '4' || version[1] == '5'))
		return false;
	std::string VersionString = "#version " + std::string(version) + '\n';
	strcpy(VERSION_STRING, VersionString.c_str());
	return true;
}

const char* ShaderProvider::GetGLSLVersion()
{
	return VERSION_STRING;
}
